package com.bdqn.filter;

import com.bdqn.base.Global;
import com.bdqn.base.R;
import com.bdqn.feign.FeignAuthService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * AuthGlobalFilter
 *
 * @author LILIBO
 * @since 2023-03-14
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 引入认证中心业务处理服务
     */
    @Resource
    private FeignAuthService feignAuthService;

    /**
     * 全局过滤器
     *
     * @param exchange 交换机
     * @param chain 过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 对认证模块的获取Token接口放行
        String uri = exchange.getRequest().getURI().getPath();
        log.debug("AuthGlobalFilter URI: {}", uri);
        if (uri.startsWith("/api/auth")) {
            // 交给下一个过滤器链
            return chain.filter(exchange);
        }

        // 获取HTTP头部的Token
        String token = exchange.getRequest().getHeaders().getFirst(Global.KEY_TOKEN);
        log.debug("AuthGlobalFilter Token: {}", token);

        // 如果Token为空
        if (StringUtils.isEmpty(token)) {
            log.info("AuthGlobalFilter Verify: (406 Not Acceptable) Token={}", token);
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete(); // 拦截请求，返回结果
        }

        R verify; // 验证结果
        // 在网关中WebFlux需要异步调用（block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3）
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<R> future = executorService.submit(() -> feignAuthService.verify(token));
        try {
            verify = future.get(); // 异步处理完毕时拿到结果
        } catch (InterruptedException | ExecutionException e) {
            verify = R.failure("系统繁忙，请稍后再试~");
        } finally {
            executorService.shutdown();
        }
        // 判断验证结果
        log.debug("AuthGlobalFilter Verify: {}", verify);
        if (verify.getCode() != 0 || !Boolean.valueOf(verify.getData().toString())) {
            // 验证不通过
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete(); // 拦截请求，返回结果
        }

        // 验证通过，交给下一个过滤器链
        return chain.filter(exchange);
    }

    /**
     * 该过滤器的优先级，越小优先级越高（注意：该系统-1表示最先执行，其他过滤器在全局过滤器的后面）
     *
     * @return
     */
    @Override
    public int getOrder() {
        return -1;
    }

}
